#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <ctime>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

#define proc_num 5

#define MakeSrand() srand((unsigned int)time(nullptr) ^ getpid() ^ 0x2024 ^ rand() % 0307)

typedef void (*func)();

void downloadTask()
{
    cout << "pid : " << getpid() << " ---> " << " 下载任务\n" << endl;
    sleep(1);
}

void ioTask()
{
    cout << "pid : " << getpid() << " ---> " << "IO任务\n" << endl;
    sleep(1);
}

void flushTask()
{
    cout << "pid : " << getpid() << "  ---> " << "刷新任务\n" << endl;
    sleep(1);
}

void networkTask()
{
    cout << "pid : " << getpid() << "  ---> " << "网络任务\n" << endl;
    sleep(1);
}

void loadTask(vector<func> *funcMap)
{
    assert(funcMap);
    funcMap->push_back(downloadTask);
    funcMap->push_back(ioTask);
    funcMap->push_back(flushTask);
    funcMap->push_back(networkTask);
}

class subproc
{
public:
    subproc(int sub_id, int write_id)
        : _sub_id(sub_id), _write_id(write_id)
    {
        char name[256];
        snprintf(name, sizeof name, "subproc_id : %d [pid : %d] [fd : %d]", _num++, _sub_id, _write_id);
        _name = name;
    }

public:
    static int _num;
    string _name;
    int _sub_id;
    int _write_id;
};

int subproc::_num = 0;

void SendTask(subproc &subprocess, int func_id)
{
    int ret = write(subprocess._write_id, &func_id, sizeof func_id);
    assert(ret == sizeof(int));
    (void)ret;
    cout << "Send task successfully! pid : " << getpid() << " ---> " << subprocess._name << endl;
}

int ReceiveTask(int read_id)
{
    int code = 0;
    int ret = read(read_id, &code, sizeof(code));
    if(ret == sizeof(int))
    {
        return code;
    }
    else if(ret == 0)
    {
        return -1;
    }
}

void createsubproc(vector<subproc> &subprocMap, vector<func> &funcMap)
{
    for (int i = 0; i < proc_num; i++)
    {
        // 给每个进程创建管道文件用来通信
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;

        // 这里其实有个bug
        /*
            当父进程创建了第二个子进程之后，第二个子进程拷贝了父进程的文件描述符表
            此时父进程的文件描述表肯定包含上一个子进程的管道文件的写端文件描述符，我们假设是3
            那么创建出来的第二个子进程也就拥有了上一个子进程的写端文件描述符3
            但是子进程只是读，并不会写，所以不会出现问题

            会出现问题的地方在父进程不派发任务然后关闭写端文件描述符那里，
            如果我按顺序遍历子进程进行写端文件描述符关闭的话，如果关一个，wait一个的话，那就会阻塞在第一个子进程那里
            因为第二个子进程还保留着上一个子进程的写端文件描述符，所以第一个子进程还在等着第二个子进程写数据呢！

            因为我们是用一次性关闭父进程全部写端文件描述符的方法，所以关到最后一个子进程的写端文件描述符后，最后一个子进程就读到0了
            然后就退了，此时它保留着的上一个进程的写端文件描述符也就被释放了，相当于子进程读到0的顺序从下往上的
        */

        // 创建子进程
        pid_t id = fork();
        assert(id >= 0);
        if (id == 0)
        {
            while (true)
            {
                // 子进程，处理任务
                close(fds[1]);

                int cmdcode = ReceiveTask(fds[0]);
                if (cmdcode >= 0 && cmdcode < funcMap.size())
                {
                    funcMap[cmdcode]();
                }
                else
                {
                    cout << "subproc_id: " << getpid() << " ---> get task error!\n" << endl;
                    break;
                }
            }

            exit(0); // 退出码设为对应进程编号
        }

        // 父进程
        close(fds[0]);

        // 记录创建的子进程相关信息
        subproc sub(id, fds[1]);
        subprocMap.push_back(sub);
    }
}

void loadBlanceControl(vector<subproc> &subprocMap, vector<func> &funcMap, int count)
{
    int sub_size = subprocMap.size();
    int func_size = funcMap.size();
    bool flag = false;
    int tmp_count = count;

    if(count == 0)
        flag = true;
    while (flag || count > 0)
    {
        // 1.选择一个子进程 ---> vector<subEp> -> index - 随机数
        int sub_id = rand() % sub_size;

        // 2.选择一个任务 ---> vector<func_t> -> index
        int func_id = rand() % func_size;

        // 3.发送任务
        SendTask(subprocMap[sub_id], func_id);
        cout << "已发送的任务个数 : " << tmp_count - (--count)  << endl;

        sleep(1);
    }

    // 父进程关闭写端
    for(int i = 0; i <sub_size; i++) close(subprocMap[i]._write_id);
}

void waitProcess(vector<subproc> &subprocMap)
{
    int subproc_size = subprocMap.size();
    for(int i = 0; i < subproc_size; i++)
    {
        int status = 0;
        waitpid(subprocMap[i]._sub_id, &status, 0);     // 阻塞时等待

        cout << "waitpid successfully! " << "subproc_id : " << subprocMap[i]._sub_id << " ---> " << "signal : " << (status & 0x7F) << " ; ";
        if(signal != 0)
            cout << "exit_code : " << ((status >> 8) & 0xFF) << endl;
    }
}

int main()
{
    // 生成伪随机数种子
    MakeSrand();

    // 1.前期准备工作 + 创建子进程 -> 执行任务
    // 1.1 记录任务
    vector<func> funcMap;
    loadTask(&funcMap);

    // 1.2 创建子进程 + 记录子进程相关信息
    vector<subproc> subprocMap;
    createsubproc(subprocMap, funcMap);

    // 2.父进程控制子进程完成任务
    int task_cnt = 3;  // 0 -> 永远执行任务
    loadBlanceControl(subprocMap, funcMap, task_cnt);

    // 3.回收子进程资源
    waitProcess(subprocMap);

    return 0;
}